mxlpy 0.21.0__py3-none-any.whl → 0.22.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
mxlpy/__init__.py CHANGED
@@ -52,6 +52,7 @@ from . import (
52
52
  plot,
53
53
  report,
54
54
  sbml,
55
+ units,
55
56
  )
56
57
  from .integrators import DefaultIntegrator, Scipy
57
58
  from .label_map import LabelMapper
@@ -61,7 +62,7 @@ from .model import Model
61
62
  from .scan import steady_state, time_course, time_course_over_protocol
62
63
  from .simulator import Simulator
63
64
  from .symbolic import SymbolicModel, to_symbolic_model
64
- from .types import Derived, IntegratorProtocol, unwrap
65
+ from .types import Derived, IntegratorProtocol, Parameter, Variable, unwrap
65
66
 
66
67
  with contextlib.suppress(ImportError):
67
68
  from .integrators import Assimulo
@@ -87,9 +88,11 @@ __all__ = [
87
88
  "LabelMapper",
88
89
  "LinearLabelMapper",
89
90
  "Model",
91
+ "Parameter",
90
92
  "Scipy",
91
93
  "Simulator",
92
94
  "SymbolicModel",
95
+ "Variable",
93
96
  "cartesian_product",
94
97
  "compare",
95
98
  "distributions",
@@ -110,6 +113,7 @@ __all__ = [
110
113
  "time_course",
111
114
  "time_course_over_protocol",
112
115
  "to_symbolic_model",
116
+ "units",
113
117
  "unwrap",
114
118
  ]
115
119
 
mxlpy/compare.py CHANGED
@@ -230,11 +230,7 @@ def protocol_time_courses(
230
230
  ) -> ProtocolComparison:
231
231
  """Compare the time courses of two models."""
232
232
  return ProtocolComparison(
233
- res1=unwrap(
234
- Simulator(m1).simulate_over_protocol(protocol=protocol).get_result()
235
- ),
236
- res2=unwrap(
237
- Simulator(m2).simulate_over_protocol(protocol=protocol).get_result()
238
- ),
233
+ res1=unwrap(Simulator(m1).simulate_protocol(protocol=protocol).get_result()),
234
+ res2=unwrap(Simulator(m2).simulate_protocol(protocol=protocol).get_result()),
239
235
  protocol=protocol,
240
236
  )
@@ -187,7 +187,7 @@ def model_diff(m1: Model, m2: Model) -> ModelDiff:
187
187
  if (v2 := m2._parameters.get(k)) is None: # noqa: SLF001
188
188
  diff.missing_parameters.add(k)
189
189
  elif v1 != v2:
190
- diff.different_parameters[k] = (v1, v2)
190
+ diff.different_parameters[k] = (v1, v2) # type: ignore
191
191
 
192
192
  for k, v1 in m1._variables.items(): # noqa: SLF001
193
193
  if (v2 := m2._variables.get(k)) is None: # noqa: SLF001
mxlpy/fit.py CHANGED
@@ -32,6 +32,7 @@ if TYPE_CHECKING:
32
32
  LOGGER = logging.getLogger(__name__)
33
33
 
34
34
  __all__ = [
35
+ "Bounds",
35
36
  "CarouselFit",
36
37
  "FitResult",
37
38
  "InitialGuess",
@@ -43,13 +44,13 @@ __all__ = [
43
44
  "ResidualFn",
44
45
  "SteadyStateResidualFn",
45
46
  "TimeSeriesResidualFn",
47
+ "carousel_protocol_time_course",
46
48
  "carousel_steady_state",
47
49
  "carousel_time_course",
48
- "carousel_time_course_over_protocol",
50
+ "protocol_time_course",
49
51
  "rmse",
50
52
  "steady_state",
51
53
  "time_course",
52
- "time_course_over_protocol",
53
54
  ]
54
55
 
55
56
 
@@ -83,7 +84,15 @@ class CarouselFit:
83
84
 
84
85
  type InitialGuess = dict[str, float]
85
86
  type ResidualFn = Callable[[Array], float]
86
- type MinimizeFn = Callable[[ResidualFn, InitialGuess], MinResult | None]
87
+ type Bounds = dict[str, tuple[float | None, float | None]]
88
+ type MinimizeFn = Callable[
89
+ [
90
+ ResidualFn,
91
+ InitialGuess,
92
+ Bounds,
93
+ ],
94
+ MinResult | None,
95
+ ]
87
96
  type LossFn = Callable[
88
97
  [
89
98
  pd.DataFrame | pd.Series,
@@ -151,7 +160,6 @@ class ProtocolResidualFn(Protocol):
151
160
  integrator: IntegratorType,
152
161
  loss_fn: LossFn,
153
162
  protocol: pd.DataFrame,
154
- time_points_per_step: int = 10,
155
163
  ) -> float:
156
164
  """Calculate residual error between model time course and experimental data."""
157
165
  ...
@@ -160,11 +168,12 @@ class ProtocolResidualFn(Protocol):
160
168
  def _default_minimize_fn(
161
169
  residual_fn: ResidualFn,
162
170
  p0: dict[str, float],
171
+ bounds: Bounds,
163
172
  ) -> MinResult | None:
164
173
  res = minimize(
165
174
  residual_fn,
166
175
  x0=list(p0.values()),
167
- bounds=[(0, None) for _ in range(len(p0))],
176
+ bounds=[bounds.get(name, (1e-6, 1e6)) for name in p0],
168
177
  method="L-BFGS-B",
169
178
  )
170
179
  if res.success:
@@ -278,7 +287,7 @@ def _time_course_residual(
278
287
  )
279
288
 
280
289
 
281
- def _protocol_residual(
290
+ def _protocol_time_course_residual(
282
291
  par_values: ArrayLike,
283
292
  # This will be filled out by partial
284
293
  par_names: list[str],
@@ -288,7 +297,6 @@ def _protocol_residual(
288
297
  integrator: IntegratorType,
289
298
  loss_fn: LossFn,
290
299
  protocol: pd.DataFrame,
291
- time_points_per_step: int = 10,
292
300
  ) -> float:
293
301
  """Calculate residual error between model time course and experimental data.
294
302
 
@@ -313,9 +321,9 @@ def _protocol_residual(
313
321
  y0=y0,
314
322
  integrator=integrator,
315
323
  )
316
- .simulate_over_protocol(
324
+ .simulate_protocol_time_course(
317
325
  protocol=protocol,
318
- time_points_per_step=time_points_per_step,
326
+ time_points=data.index,
319
327
  )
320
328
  .get_result()
321
329
  )
@@ -329,6 +337,84 @@ def _protocol_residual(
329
337
  )
330
338
 
331
339
 
340
+ def _carousel_steady_state_worker(
341
+ model: Model,
342
+ p0: dict[str, float],
343
+ data: pd.Series,
344
+ y0: dict[str, float] | None,
345
+ integrator: IntegratorType | None,
346
+ loss_fn: LossFn,
347
+ minimize_fn: MinimizeFn,
348
+ residual_fn: SteadyStateResidualFn,
349
+ bounds: Bounds | None,
350
+ ) -> FitResult | None:
351
+ model_pars = model.get_parameter_values()
352
+
353
+ return steady_state(
354
+ model,
355
+ p0={k: v for k, v in p0.items() if k in model_pars},
356
+ y0=y0,
357
+ data=data,
358
+ minimize_fn=minimize_fn,
359
+ residual_fn=residual_fn,
360
+ integrator=integrator,
361
+ loss_fn=loss_fn,
362
+ bounds=bounds,
363
+ )
364
+
365
+
366
+ def _carousel_time_course_worker(
367
+ model: Model,
368
+ p0: dict[str, float],
369
+ data: pd.DataFrame,
370
+ y0: dict[str, float] | None,
371
+ integrator: IntegratorType | None,
372
+ loss_fn: LossFn,
373
+ minimize_fn: MinimizeFn,
374
+ residual_fn: TimeSeriesResidualFn,
375
+ bounds: Bounds | None,
376
+ ) -> FitResult | None:
377
+ model_pars = model.get_parameter_values()
378
+ return time_course(
379
+ model,
380
+ p0={k: v for k, v in p0.items() if k in model_pars},
381
+ y0=y0,
382
+ data=data,
383
+ minimize_fn=minimize_fn,
384
+ residual_fn=residual_fn,
385
+ integrator=integrator,
386
+ loss_fn=loss_fn,
387
+ bounds=bounds,
388
+ )
389
+
390
+
391
+ def _carousel_protocol_worker(
392
+ model: Model,
393
+ p0: dict[str, float],
394
+ data: pd.DataFrame,
395
+ protocol: pd.DataFrame,
396
+ y0: dict[str, float] | None,
397
+ integrator: IntegratorType | None,
398
+ loss_fn: LossFn,
399
+ minimize_fn: MinimizeFn,
400
+ residual_fn: ProtocolResidualFn,
401
+ bounds: Bounds | None,
402
+ ) -> FitResult | None:
403
+ model_pars = model.get_parameter_values()
404
+ return protocol_time_course(
405
+ model,
406
+ p0={k: v for k, v in p0.items() if k in model_pars},
407
+ y0=y0,
408
+ protocol=protocol,
409
+ data=data,
410
+ minimize_fn=minimize_fn,
411
+ residual_fn=residual_fn,
412
+ integrator=integrator,
413
+ loss_fn=loss_fn,
414
+ bounds=bounds,
415
+ )
416
+
417
+
332
418
  def steady_state(
333
419
  model: Model,
334
420
  *,
@@ -339,6 +425,7 @@ def steady_state(
339
425
  residual_fn: SteadyStateResidualFn = _steady_state_residual,
340
426
  integrator: IntegratorType | None = None,
341
427
  loss_fn: LossFn = rmse,
428
+ bounds: Bounds | None = None,
342
429
  ) -> FitResult | None:
343
430
  """Fit model parameters to steady-state experimental data.
344
431
 
@@ -355,18 +442,19 @@ def steady_state(
355
442
  residual_fn: Function to calculate fitting error
356
443
  integrator: ODE integrator class
357
444
  loss_fn: Loss function to use for residual calculation
445
+ bounds: Mapping of bounds per parameter
358
446
 
359
447
  Returns:
360
448
  dict[str, float]: Fitted parameters as {parameter_name: fitted_value}
361
449
 
362
450
  Note:
363
- Uses L-BFGS-B optimization with bounds [1e-12, 1e6] for all parameters
451
+ Uses L-BFGS-B optimization with bounds [1e-6, 1e6] for all parameters
364
452
 
365
453
  """
366
454
  par_names = list(p0.keys())
367
455
 
368
456
  # Copy to restore
369
- p_orig = model.parameters
457
+ p_orig = model.get_parameter_values()
370
458
 
371
459
  fn = cast(
372
460
  ResidualFn,
@@ -380,7 +468,7 @@ def steady_state(
380
468
  loss_fn=loss_fn,
381
469
  ),
382
470
  )
383
- min_result = minimize_fn(fn, p0)
471
+ min_result = minimize_fn(fn, p0, {} if bounds is None else bounds)
384
472
  # Restore original model
385
473
  model.update_parameters(p_orig)
386
474
  if min_result is None:
@@ -403,6 +491,7 @@ def time_course(
403
491
  residual_fn: TimeSeriesResidualFn = _time_course_residual,
404
492
  integrator: IntegratorType | None = None,
405
493
  loss_fn: LossFn = rmse,
494
+ bounds: Bounds | None = None,
406
495
  ) -> FitResult | None:
407
496
  """Fit model parameters to time course of experimental data.
408
497
 
@@ -419,16 +508,17 @@ def time_course(
419
508
  residual_fn: Function to calculate fitting error
420
509
  integrator: ODE integrator class
421
510
  loss_fn: Loss function to use for residual calculation
511
+ bounds: Mapping of bounds per parameter
422
512
 
423
513
  Returns:
424
514
  dict[str, float]: Fitted parameters as {parameter_name: fitted_value}
425
515
 
426
516
  Note:
427
- Uses L-BFGS-B optimization with bounds [1e-12, 1e6] for all parameters
517
+ Uses L-BFGS-B optimization with bounds [1e-6, 1e6] for all parameters
428
518
 
429
519
  """
430
520
  par_names = list(p0.keys())
431
- p_orig = model.parameters
521
+ p_orig = model.get_parameter_values()
432
522
 
433
523
  fn = cast(
434
524
  ResidualFn,
@@ -443,7 +533,7 @@ def time_course(
443
533
  ),
444
534
  )
445
535
 
446
- min_result = minimize_fn(fn, p0)
536
+ min_result = minimize_fn(fn, p0, {} if bounds is None else bounds)
447
537
  # Restore original model
448
538
  model.update_parameters(p_orig)
449
539
  if min_result is None:
@@ -456,7 +546,7 @@ def time_course(
456
546
  )
457
547
 
458
548
 
459
- def time_course_over_protocol(
549
+ def protocol_time_course(
460
550
  model: Model,
461
551
  *,
462
552
  p0: dict[str, float],
@@ -464,10 +554,10 @@ def time_course_over_protocol(
464
554
  protocol: pd.DataFrame,
465
555
  y0: dict[str, float] | None = None,
466
556
  minimize_fn: MinimizeFn = _default_minimize_fn,
467
- residual_fn: ProtocolResidualFn = _protocol_residual,
557
+ residual_fn: ProtocolResidualFn = _protocol_time_course_residual,
468
558
  integrator: IntegratorType | None = None,
469
559
  loss_fn: LossFn = rmse,
470
- time_points_per_step: int = 10,
560
+ bounds: Bounds | None = None,
471
561
  ) -> FitResult | None:
472
562
  """Fit model parameters to time course of experimental data.
473
563
 
@@ -486,16 +576,17 @@ def time_course_over_protocol(
486
576
  integrator: ODE integrator class
487
577
  loss_fn: Loss function to use for residual calculation
488
578
  time_points_per_step: Number of time points per step in the protocol
579
+ bounds: Mapping of bounds per parameter
489
580
 
490
581
  Returns:
491
582
  dict[str, float]: Fitted parameters as {parameter_name: fitted_value}
492
583
 
493
584
  Note:
494
- Uses L-BFGS-B optimization with bounds [1e-12, 1e6] for all parameters
585
+ Uses L-BFGS-B optimization with bounds [1e-6, 1e6] for all parameters
495
586
 
496
587
  """
497
588
  par_names = list(p0.keys())
498
- p_orig = model.parameters
589
+ p_orig = model.get_parameter_values()
499
590
 
500
591
  fn = cast(
501
592
  ResidualFn,
@@ -508,11 +599,10 @@ def time_course_over_protocol(
508
599
  integrator=integrator,
509
600
  loss_fn=loss_fn,
510
601
  protocol=protocol,
511
- time_points_per_step=time_points_per_step,
512
602
  ),
513
603
  )
514
604
 
515
- min_result = minimize_fn(fn, p0)
605
+ min_result = minimize_fn(fn, p0, {} if bounds is None else bounds)
516
606
  # Restore original model
517
607
  model.update_parameters(p_orig)
518
608
  if min_result is None:
@@ -525,78 +615,6 @@ def time_course_over_protocol(
525
615
  )
526
616
 
527
617
 
528
- def _carousel_steady_state_worker(
529
- model: Model,
530
- p0: dict[str, float],
531
- data: pd.Series,
532
- y0: dict[str, float] | None,
533
- integrator: IntegratorType | None,
534
- loss_fn: LossFn,
535
- minimize_fn: MinimizeFn,
536
- residual_fn: SteadyStateResidualFn,
537
- ) -> FitResult | None:
538
- model_pars = model.parameters
539
-
540
- return steady_state(
541
- model,
542
- p0={k: v for k, v in p0.items() if k in model_pars},
543
- y0=y0,
544
- data=data,
545
- minimize_fn=minimize_fn,
546
- residual_fn=residual_fn,
547
- integrator=integrator,
548
- loss_fn=loss_fn,
549
- )
550
-
551
-
552
- def _carousel_time_course_worker(
553
- model: Model,
554
- p0: dict[str, float],
555
- data: pd.DataFrame,
556
- y0: dict[str, float] | None,
557
- integrator: IntegratorType | None,
558
- loss_fn: LossFn,
559
- minimize_fn: MinimizeFn,
560
- residual_fn: TimeSeriesResidualFn,
561
- ) -> FitResult | None:
562
- model_pars = model.parameters
563
- return time_course(
564
- model,
565
- p0={k: v for k, v in p0.items() if k in model_pars},
566
- y0=y0,
567
- data=data,
568
- minimize_fn=minimize_fn,
569
- residual_fn=residual_fn,
570
- integrator=integrator,
571
- loss_fn=loss_fn,
572
- )
573
-
574
-
575
- def _carousel_protocol_worker(
576
- model: Model,
577
- p0: dict[str, float],
578
- data: pd.DataFrame,
579
- protocol: pd.DataFrame,
580
- y0: dict[str, float] | None,
581
- integrator: IntegratorType | None,
582
- loss_fn: LossFn,
583
- minimize_fn: MinimizeFn,
584
- residual_fn: ProtocolResidualFn,
585
- ) -> FitResult | None:
586
- model_pars = model.parameters
587
- return time_course_over_protocol(
588
- model,
589
- p0={k: v for k, v in p0.items() if k in model_pars},
590
- y0=y0,
591
- protocol=protocol,
592
- data=data,
593
- minimize_fn=minimize_fn,
594
- residual_fn=residual_fn,
595
- integrator=integrator,
596
- loss_fn=loss_fn,
597
- )
598
-
599
-
600
618
  def carousel_steady_state(
601
619
  carousel: Carousel,
602
620
  *,
@@ -607,8 +625,33 @@ def carousel_steady_state(
607
625
  residual_fn: SteadyStateResidualFn = _steady_state_residual,
608
626
  integrator: IntegratorType | None = None,
609
627
  loss_fn: LossFn = rmse,
628
+ bounds: Bounds | None = None,
610
629
  ) -> CarouselFit:
611
- """Fit model parameters to steady-state experimental data over a carousel."""
630
+ """Fit model parameters to steady-state experimental data over a carousel.
631
+
632
+ Examples:
633
+ >>> carousel_steady_state(carousel, p0=p0, data=data)
634
+
635
+ Args:
636
+ carousel: Model carousel to fit
637
+ p0: Initial parameter guesses as {parameter_name: value}
638
+ data: Experimental time course data
639
+ protocol: Experimental protocol
640
+ y0: Initial conditions as {species_name: value}
641
+ minimize_fn: Function to minimize fitting error
642
+ residual_fn: Function to calculate fitting error
643
+ integrator: ODE integrator class
644
+ loss_fn: Loss function to use for residual calculation
645
+ time_points_per_step: Number of time points per step in the protocol
646
+ bounds: Mapping of bounds per parameter
647
+
648
+ Returns:
649
+ dict[str, float]: Fitted parameters as {parameter_name: fitted_value}
650
+
651
+ Note:
652
+ Uses L-BFGS-B optimization with bounds [1e-6, 1e6] for all parameters
653
+
654
+ """
612
655
  return CarouselFit(
613
656
  [
614
657
  fit
@@ -622,6 +665,7 @@ def carousel_steady_state(
622
665
  loss_fn=loss_fn,
623
666
  minimize_fn=minimize_fn,
624
667
  residual_fn=residual_fn,
668
+ bounds=bounds,
625
669
  ),
626
670
  inputs=list(enumerate(carousel.variants)),
627
671
  )
@@ -640,8 +684,33 @@ def carousel_time_course(
640
684
  residual_fn: TimeSeriesResidualFn = _time_course_residual,
641
685
  integrator: IntegratorType | None = None,
642
686
  loss_fn: LossFn = rmse,
687
+ bounds: Bounds | None = None,
643
688
  ) -> CarouselFit:
644
- """Fit model parameters to time course of experimental data over a carousel."""
689
+ """Fit model parameters to time course of experimental data over a carousel.
690
+
691
+ Examples:
692
+ >>> carousel_steady_state(carousel, p0=p0, data=data)
693
+
694
+ Args:
695
+ carousel: Model carousel to fit
696
+ p0: Initial parameter guesses as {parameter_name: value}
697
+ data: Experimental time course data
698
+ protocol: Experimental protocol
699
+ y0: Initial conditions as {species_name: value}
700
+ minimize_fn: Function to minimize fitting error
701
+ residual_fn: Function to calculate fitting error
702
+ integrator: ODE integrator class
703
+ loss_fn: Loss function to use for residual calculation
704
+ time_points_per_step: Number of time points per step in the protocol
705
+ bounds: Mapping of bounds per parameter
706
+
707
+ Returns:
708
+ dict[str, float]: Fitted parameters as {parameter_name: fitted_value}
709
+
710
+ Note:
711
+ Uses L-BFGS-B optimization with bounds [1e-6, 1e6] for all parameters
712
+
713
+ """
645
714
  return CarouselFit(
646
715
  [
647
716
  fit
@@ -655,6 +724,7 @@ def carousel_time_course(
655
724
  loss_fn=loss_fn,
656
725
  minimize_fn=minimize_fn,
657
726
  residual_fn=residual_fn,
727
+ bounds=bounds,
658
728
  ),
659
729
  inputs=list(enumerate(carousel.variants)),
660
730
  )
@@ -663,7 +733,7 @@ def carousel_time_course(
663
733
  )
664
734
 
665
735
 
666
- def carousel_time_course_over_protocol(
736
+ def carousel_protocol_time_course(
667
737
  carousel: Carousel,
668
738
  *,
669
739
  p0: dict[str, float],
@@ -671,11 +741,36 @@ def carousel_time_course_over_protocol(
671
741
  protocol: pd.DataFrame,
672
742
  y0: dict[str, float] | None = None,
673
743
  minimize_fn: MinimizeFn = _default_minimize_fn,
674
- residual_fn: ProtocolResidualFn = _protocol_residual,
744
+ residual_fn: ProtocolResidualFn = _protocol_time_course_residual,
675
745
  integrator: IntegratorType | None = None,
676
746
  loss_fn: LossFn = rmse,
747
+ bounds: Bounds | None = None,
677
748
  ) -> CarouselFit:
678
- """Fit model parameters to time course of experimental data over a protocol."""
749
+ """Fit model parameters to time course of experimental data over a protocol.
750
+
751
+ Examples:
752
+ >>> carousel_steady_state(carousel, p0=p0, data=data)
753
+
754
+ Args:
755
+ carousel: Model carousel to fit
756
+ p0: Initial parameter guesses as {parameter_name: value}
757
+ data: Experimental time course data
758
+ protocol: Experimental protocol
759
+ y0: Initial conditions as {species_name: value}
760
+ minimize_fn: Function to minimize fitting error
761
+ residual_fn: Function to calculate fitting error
762
+ integrator: ODE integrator class
763
+ loss_fn: Loss function to use for residual calculation
764
+ time_points_per_step: Number of time points per step in the protocol
765
+ bounds: Mapping of bounds per parameter
766
+
767
+ Returns:
768
+ dict[str, float]: Fitted parameters as {parameter_name: fitted_value}
769
+
770
+ Note:
771
+ Uses L-BFGS-B optimization with bounds [1e-6, 1e6] for all parameters
772
+
773
+ """
679
774
  return CarouselFit(
680
775
  [
681
776
  fit
@@ -690,6 +785,7 @@ def carousel_time_course_over_protocol(
690
785
  loss_fn=loss_fn,
691
786
  minimize_fn=minimize_fn,
692
787
  residual_fn=residual_fn,
788
+ bounds=bounds,
693
789
  ),
694
790
  inputs=list(enumerate(carousel.variants)),
695
791
  )
mxlpy/identify.py CHANGED
@@ -59,21 +59,26 @@ def profile_likelihood(
59
59
 
60
60
  """
61
61
  parameter_distributions = sample(
62
- {k: LogNormal(np.log(v), sigma=1) for k, v in model.parameters.items()},
62
+ {
63
+ k: LogNormal(np.log(v), sigma=1)
64
+ for k, v in model.get_parameter_values().items()
65
+ },
63
66
  n=n_random,
64
67
  )
65
68
 
66
69
  res = {}
67
70
  for value in tqdm(parameter_values, desc=parameter_name):
68
71
  model.update_parameter(parameter_name, value)
69
- res[value] = parallelise(
70
- partial(
71
- _mc_fit_time_course_worker, model=model, data=data, loss_fn=loss_fn
72
- ),
73
- inputs=list(
74
- parameter_distributions.drop(columns=parameter_name).iterrows()
75
- ),
76
- disable_tqdm=True,
72
+ res[value] = dict(
73
+ parallelise(
74
+ partial(
75
+ _mc_fit_time_course_worker, model=model, data=data, loss_fn=loss_fn
76
+ ),
77
+ inputs=list(
78
+ parameter_distributions.drop(columns=parameter_name).iterrows()
79
+ ),
80
+ disable_tqdm=True,
81
+ )
77
82
  )
78
83
  errors = pd.DataFrame(res, dtype=float).T.abs().mean(axis=1)
79
84
  errors.index.name = "fitting error"
@@ -98,6 +98,9 @@ class Scipy:
98
98
  tuple[ArrayLike, ArrayLike]: Tuple containing the time points and the integrated values.
99
99
 
100
100
  """
101
+ if time_points[0] != self.t0:
102
+ time_points = np.insert(time_points, 0, self.t0)
103
+
101
104
  res = spi.solve_ivp(
102
105
  fun=self.rhs,
103
106
  y0=self.y0,
mxlpy/label_map.py CHANGED
@@ -551,13 +551,13 @@ class LabelMapper:
551
551
 
552
552
  m = Model()
553
553
 
554
- m.add_parameters(self.model.parameters)
554
+ m.add_parameters(self.model.get_parameter_values())
555
555
 
556
- for name, dp in self.model.derived_parameters.items():
556
+ for name, dp in self.model.get_derived_parameters().items():
557
557
  m.add_derived(name, fn=dp.fn, args=dp.args)
558
558
 
559
559
  variables: dict[str, float | Derived] = {}
560
- for k, v in self.model.variables.items():
560
+ for k, v in self.model.get_initial_conditions().items():
561
561
  if (isos := isotopomers.get(k)) is None:
562
562
  variables[k] = v
563
563
  else:
@@ -585,14 +585,14 @@ class LabelMapper:
585
585
  args=label_names,
586
586
  )
587
587
 
588
- for name, dv in self.model.derived_variables.items():
588
+ for name, dv in self.model.get_derived_variables().items():
589
589
  m.add_derived(
590
590
  name,
591
591
  fn=dv.fn,
592
592
  args=[f"{i}__total" if i in isotopomers else i for i in dv.args],
593
593
  )
594
594
 
595
- for rxn_name, rxn in self.model.reactions.items():
595
+ for rxn_name, rxn in self.model.get_raw_reactions().items():
596
596
  if (label_map := self.label_maps.get(rxn_name)) is None:
597
597
  m.add_reaction(
598
598
  rxn_name,
mxlpy/linear_label_map.py CHANGED
@@ -272,8 +272,10 @@ class LinearLabelMapper:
272
272
  m = Model()
273
273
  m.add_variables(variables)
274
274
  m.add_parameters(concs.to_dict() | fluxes.to_dict() | {"EXT": external_label})
275
+
276
+ rxns = self.model.get_raw_reactions()
275
277
  for rxn_name, label_map in self.label_maps.items():
276
- rxn = self.model.reactions[rxn_name]
278
+ rxn = rxns[rxn_name]
277
279
  subs, prods = _unpack_stoichiometries(rxn.stoichiometry)
278
280
 
279
281
  subs = _stoichiometry_to_duplicate_list(subs)
mxlpy/mc.py CHANGED
@@ -66,6 +66,7 @@ class ParameterScanWorker(Protocol):
66
66
  model: Model,
67
67
  *,
68
68
  parameters: pd.DataFrame,
69
+ y0: dict[str, float] | None,
69
70
  rel_norm: bool,
70
71
  integrator: IntegratorType,
71
72
  ) -> SteadyStates:
@@ -77,6 +78,7 @@ def _parameter_scan_worker(
77
78
  model: Model,
78
79
  *,
79
80
  parameters: pd.DataFrame,
81
+ y0: dict[str, float] | None,
80
82
  rel_norm: bool,
81
83
  integrator: IntegratorType,
82
84
  ) -> SteadyStates:
@@ -109,6 +111,7 @@ def _parameter_scan_worker(
109
111
  parallel=False,
110
112
  rel_norm=rel_norm,
111
113
  integrator=integrator,
114
+ y0=y0,
112
115
  )
113
116
 
114
117